package com.tools.web.interceptor.quota;

import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 使用 RedisTemplate 客户端操作 Redis 实现配额限制拦截器
 * */
@Note("使用 RedisTemplate 客户端操作 Redis 实现配额限制拦截器")
public final class RedisTemplateQuotaManager extends AbsQuotaManager implements QuotaRedisLuaScript {

    @Note("操作 Redis 的客户端")
    private final RedisTemplate<String, String> redisTemplate;

    public RedisTemplateQuotaManager(RedisTemplate<String, String> redisTemplate, QuotaSource source) {
        super(source);
        this.redisTemplate = Objects.requireNonNull(redisTemplate, "操作 Redis 集群的客户端 RedisTemplate<String, String> 不能为 null");
    }


    @Override
    protected boolean protected_checkIsAllow(QuotaLimit quotaLimit, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String action = quotaLimit.action();
        int allow = quotaLimit.allow();
        String uniqueID = super.protected_getUniqueID(quotaLimit, request, response);
        String key = super.protected_getKey(action, uniqueID);
        Integer result = this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                CHECK_ALLOW.getBytes(),
                ReturnType.INTEGER,
                1,
                key.getBytes(),
                String.valueOf(allow).getBytes()
        ));
        if(result != null && result == 1) return true;
        response.getWriter().write(super.protected_getQuotaErrorMessage(quotaLimit, uniqueID));
        return false;
    }

    @Override
    public void increaseQuota(String action, Object uniqueID, long expiration, Timeunit timeunit) {
        String key = super.protected_getKey(action, uniqueID.toString());
        long expirationSecond = Timeunit.toSecond(expiration, timeunit);
        this.redisTemplate.execute((RedisConnection connection) -> connection.eval(
                INCREASE.getBytes(),
                ReturnType.INTEGER,
                1,
                key.getBytes(),
                String.valueOf(expirationSecond).getBytes()
        ));
    }
}
